/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

package lime2;
import lights.adapters.*;
import lights.interfaces.*;

/**
 * Wraps a tuple with Lime system information. System information consists of a TupleID, a current Location and
 * a destination Location (see the Lime papers). 
 * <br>
 * LimeWrapper has two children classes, <i>LimeTemplate</i> and <i>LimeTuple</i>. They are very similar but we keep
 * them <u>logically separated</u> because they play different roles: LimeTemplates carry data from the user to the
 * Lime system, whereas LimeTuples carry data back from the Lime system to the user.
 * <br>
 * Lime internally translates a null value for the TupleID field into "TupleID.UNSPECIFIED"; see LimeTemplate.
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

public class LimeWrapper implements java.io.Serializable
{
 /**
	 * 
	 */
	private static final long serialVersionUID = 980150075869119644L;

/**
  * Encapsulated ITuple.
  */ 	
 public ITuple tuple;
 
 /**
  * System information: TupleID.
  */
 public TupleID id;
 
 /**
  * System information: current Location.
  */ 
 public Location cur;
 
 /**
  * System information: destination Location.
  */
 public Location dest;

 /**
  * Constructor method.
  */
 public LimeWrapper()
 {
  // empty constructor	
 }
 
 /**
  * Constructor method. 
  * 
  * @param id the encapsulated TupleID 
  * @param cur the encapsulated current Location 
  * @param dest the encapsulated destination Location 
  * @param tuple the encapsulated ITuple
  */
 public LimeWrapper(TupleID id, Location cur, Location dest, ITuple tuple)
 {
  this.id = id;
  this.cur = cur;
  this.dest = dest;
  this.tuple = tuple;
 }
 
 /** 
  * Converts a LimeWrapper into a "limified" ITuple. 
  * In a limified tuple, aka flattened (or flat) tuple, the first field is the TupleID, second is the current 
  * Location, third is the destination Location and the remaining fields are the contents of the original tuple. 
  * The ITuple returned is a shallow copy of the ITuple contained in the LimeWrapper.
  * <br>
  * Please note that a null ID of the tuple is translated into the formal TupleID.class: a null value for ID plays
  * the same role Location.UNSPECIFIED plays for current and destination locations. See LimeTemplate.
  * 
  * @return "limified" ITuple
  */
 public ITuple limifyWrapper() 
 {	
  ITuple t = LimeTupleSpace.copy(tuple);
   
  // the first field is "id"  
  if (id == null) t.insertAt(new Field().setType(TupleID.class), LimeConstants.ID_TAG);
  else t.insertAt(new Field().setValue(id), LimeConstants.ID_TAG);

  // the second field is "cur"
  if (cur.isUnspecified()) t.insertAt(new Field().setType(Location.class), LimeConstants.CUR_TAG);
  else t.insertAt(new Field().setValue(cur), LimeConstants.CUR_TAG);
    
  // the third field is "dest"
  if (dest.isUnspecified()) t.insertAt(new Field().setType(Location.class), LimeConstants.DEST_TAG);
  else t.insertAt(new Field().setValue(dest), LimeConstants.DEST_TAG);

  return t;
 }

 /** 
  * Converts a "limified" (flattened) ITuple into a LimeWrapper. If any of the fields are not of the proper type, 
  * (e.g., first field is TupleID, second is Location, and third is Location), <code>null</code> is returned. 
  * The ITuple within the returned LimeWrapper is a shallow copy of the tuple passed as a parameter.
  * <br>
  * Please note that the if the TupleID field of the limified tuple is formal, the returned LimeWrapper has null 
  * value for the TupleID field. A null value for ID plays the same role Location.UNSPECIFIED plays for current and 
  * destination locations. See LimeTemplate.
  * 
  * @param flat the "limified" ITuple
  * @return LimeWrapper the corresponding LimeWrapper
  */ 
 public static LimeWrapper delimifyWrapper(ITuple flat) 
 {
  // the LimeWrapper to return
  LimeWrapper lt = null;
  
  // auxiliary variables
  TupleID i;
  Location c;
  Location d;
  ITuple t;
  IValuedField f;

  // checks whether this is a limified ITuple )
  if (
      flat != null && 
      flat.get(LimeConstants.ID_TAG).getType().equals(TupleID.class) &&
      flat.get(LimeConstants.CUR_TAG).getType().equals(Location.class) &&
      flat.get(LimeConstants.DEST_TAG).getType().equals(Location.class)
     ) 
     {
	  // extracts "id"	 
 	  f = (IValuedField) flat.get(LimeConstants.ID_TAG);
      if (f.isFormal()) i = null; else i = (TupleID) f.getValue();
      
      // extracts "cur"
	  f = (IValuedField) flat.get(LimeConstants.CUR_TAG); 
	  if (f.isFormal()) c = Location.UNSPECIFIED; else c = (Location) f.getValue();

      // extracts "dest" 
	  f = (IValuedField) flat.get(LimeConstants.DEST_TAG);
	  if (f.isFormal()) d = Location.UNSPECIFIED; else d = (Location) f.getValue();

      // extracts the non "system information" fields from the flattened ITuple
      t = LimeTupleSpace.copy(flat, LimeConstants.TAGS_NUMBER, flat.length());  

      // creates the LimeWrapper 
	  lt = new LimeWrapper(i, c, d, t);
     }
   
  // returns the LimeWrapper
  return lt;
 }

 /**
  * Returns the TupleID of a limified tuple.
  * 
  * @param flat limified tuple
  * @return TupleID
  */
 public static TupleID getID(ITuple flat)
 {
  // if this method is invoked on tuples of the tuplespace they cannot have "unspecified" TupleID (guaranteed)	
  // (pay attention if you invoke it on the template of a reaction)	
  return (TupleID) ((IValuedField) flat.get(LimeConstants.ID_TAG)).getValue();
 }

 /**
  * Returns the current Location of a limified tuple.
  * 
  * @param flat limified tuple
  * @return current Location
  */
 public static Location getCur(ITuple flat)
 {
  IValuedField tmp = (IValuedField) flat.get(LimeConstants.CUR_TAG);
  if (tmp.isFormal()) return Location.UNSPECIFIED; else return (Location) tmp.getValue(); 
 }
 
 /**
  * Returns the destination Location of a limified tuple.
  * 
  * @param flat limified tuple
  * @return destination Location
  */
 public static Location getDest(ITuple flat)
 {
  IValuedField tmp = (IValuedField) flat.get(LimeConstants.DEST_TAG);
  if (tmp.isFormal()) return Location.UNSPECIFIED; else return (Location) tmp.getValue();
 }
 
 /**
  * Changes the current Locations of the given limified tuples to HERE.
  *  
  * @param flat array of limified tuples
  * @return the array of tuples
  */
 public static ITuple[] setCurHere(ITuple[] flat)
 {
  if (flat == null) return null;	
  for (int i = 0; i < flat.length; i++) flat[i].set(new Field().setValue(Location.HERE), LimeConstants.CUR_TAG);
  return flat;
 }
 
 /**
  * Changes the destination Locations of the given limified tuples to the given one.
  *  
  * @param dest new destination Location
  * @param flat array of limified tuples
  * @return the array of tuples
  */
 public static ITuple[] setDest(Location dest, ITuple[] flat)
 {
  if (flat == null) return null;	
  for (int i = 0; i < flat.length; i++) flat[i].set(new Field().setValue(dest), LimeConstants.DEST_TAG);
  return flat;
 }
 public String toString() 
 {
  return "[LimeWrapper]" +
         " tid="  + (id == null ? "null" : id.toString()) + 
         " cur="  + (cur == null ? "null" : cur.toString()) + 
         " dest=" + (dest == null ? "null" : dest.toString()) +
         " tup="  + (tuple == null ? "null" : tuple.toString());
 }
 
}
